/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include "collections.h"
#include "config.h"
#include "feature.h"
#include "song.h"
#include "album.h"
#include "debug.h"
#include <time.h>


static void __get_all_songs_in_directory(Collections *C, const char *directory);
static void __directory_builder__(Collections *C, Song *S);

/**
 * @brief Get all songs from the PATH in config
 */
Collections *get_all_songs() {
    Collections *C = (Collections *)calloc(1, sizeof(Collections));
    char const *PATH;
    INIT_LIST_HEAD(&C->all_songs_head);
    INIT_LIST_HEAD(&C->albums_head);
    INIT_LIST_HEAD(&C->play_list_head);
    INIT_LIST_HEAD(&C->played_list_head);
    INIT_LIST_HEAD(&C->shuffle_list_head);
    char dir[1024];
    int i = 0;
    do {
        PATH = mokoko_feature_value_get(NULL, "PATH", i);
        if (!PATH)
            return C;
        if (*PATH == '~') {
            snprintf(dir, sizeof(dir), "%s%s", getenv("HOME"), (PATH + 1));
        } else {
            snprintf(dir, sizeof(dir), "%s", PATH);
        }
        __get_all_songs_in_directory(C, dir);
        i++;
    } while (PATH);
    return NULL;
}


/**
 * @brief Free the collections
 */
void collections_free(Collections *C) {
    Album *A_ptr, *A_n;
    Song *S_ptr, *S_n;
    list_for_each_entry_safe(A_ptr, A_n, &C->albums_head, node) {
        album_free(A_ptr);
    }
    list_for_each_entry_safe(S_ptr, S_n, &C->all_songs_head, all_song_node) {
        song_free(S_ptr);
    }
    free(C);
}

/**
 * @brief Creating album by the configuration. default is by dir
 */
void create_album(Collections *C) {
    Song *ptr, *n;
    void (*builder) (Collections *C, Song *S);
    if (album_construct_type_get() == DIRECTORY) {
        builder = __directory_builder__;
    }

    if (builder)  {
        list_for_each_entry_safe(ptr, n, &C->all_songs_head, all_song_node) {
            (*builder)(C, ptr);
        }
    }

}

/**
 * @brief search the album by the dir name
 */
Album * seach_album_by_dir (Collections *C, const char *dir) {
    Album *ptr, *n;
    list_for_each_entry_safe(ptr, n, &C->albums_head, node) {
        if (!strcmp(ptr->dir, dir))
            return ptr;
    }
    return NULL;
}

/**
 * @Shuffle the list using Knuth shuffle algorithm
 */
int shuffle_list_shuffle(Collections *C) {
    struct list_head *list;
    struct list_head *list2;
    struct list_head *shuffle_list;
    int count; 
    int index;
    int i;
    int random;
    
    if (!C) {
        error("Bad parameter!!\n");
        return -1;
    }
    
    shuffle_list = (&C->shuffle_list_head);
    count = 0;
    list_for_each(list, shuffle_list) {
        count++;
    }
    if (count <= 1) 
        return 0;
    srand((unsigned)time(NULL));
    for(index = 0; index < count; index++) {
        i=0;
        list_for_each(list, shuffle_list) {
            if(i==index) break;
            i++;
        }
        random = rand() % count;
        while(random == index) {
            random = rand() % count;
        }
        i = 0;
        list_for_each(list2, shuffle_list) {
             if(i==random) break;
             i++;
        }
        list_move(list, list2);
    }
    return 0;
}

int play_list_add_song(Collections *C, Song *S) {
    if (!C || !S) {
        error("Bad parameter!!\n");
        return -1;
    }
    if (S->play_list_node.prev != &S->play_list_node) {
        error("The Song %s is already in some list!!\n", S->file);
        return -1;
    }
    list_add_tail(&S->play_list_node, &C->play_list_head);
    return 0;
}

int play_list_add_album(Collections *C, Album *A) {
    Song *S, *Sn;
    int err;
    if (!C || !A) {
        error("Bad parameter!!\n");
        return -1;
    }
    list_for_each_entry_safe(S, Sn, &A->songs_head, album_node) {
        err = play_list_add_song(C, S);
        if (err) {
            debug("Stop adding Album %s!!\n", A->name);
            return -1;
        }
    } 
    return 0;
}

int play_list_remove_song(Collections *C, Song *S) {
    if (!C || !S) {
        error("Bad parameter!!\n");
        return -1;
    }
    list_del_init(&S->play_list_node);
    return 0;
}

int play_list_remove_album(Collections *C, Album *A) {
    Song *S, *Sn;
    int err;
    if (!C || !A) {
        error("Bad parameter!!\n");
        return -1;
    }
    list_for_each_entry_safe(S, Sn, &A->songs_head, album_node) {
        err = play_list_remove_song(C, S);
        if (err) {
            debug("Stop adding Album %s!!\n", A->name);
            return -1;
        }
    } 
    return 0;
}

int move_song_to_played_list(Collections *C, Song *S) {
    int err;
    if (!C || !S) {
        error("Bad parameter!!\n");
        return -1;
    }
    err = play_list_remove_song(C, S);
    if (err) {
        return -1;
    }
    list_add_tail(&S->play_list_node, &C->played_list_head);
    return 0;
}

int play_played_list_clear(Collections *C) {
    Song *S, *Sn;
    list_for_each_entry_safe(S, Sn, &C->all_songs_head, all_song_node) {
        list_del_init(&S->play_list_node);
    }
    return 0;
}

// ----------- Internal functions -----------------
static char * check_file_mine_support (const char *file) {
    int i;
    char buf[32], *ptr; 
    const char *s;
    Feature *F = mokoko_feature_search(NULL, "SUPPORT_MIME");
    if (!F) {
        mokoko_feature_value_add(NULL, "SUPPORT_MIME", "mp3");
        mokoko_feature_value_add(NULL, "SUPPORT_MIME", "ogg");
        F = mokoko_feature_search(NULL, "SUPPORT_MIME");
    }
    for(i=0;i< F->length;i++) {
        s = mokoko_feature_value_get(NULL, "SUPPORT_MIME", i);
        if (!s) {
            error("Cannot get support Mime!!\n");
            return NULL;
        }
        snprintf(buf, sizeof(buf), ".%s", s);
        ptr = strcasestr(file, buf);
        if (ptr)
            return ptr;
    }
    return NULL;
}

static void __directory_builder__(Collections *C, Song *S) {
    static Album *album_cache = NULL;
    char *_dir;
    char *dname;
    if (!S)
        return;
    _dir = song_dirname(S);
    if (!_dir) {
        error("dir is NULL!!\n");
        return;
    }
    dname = strdupa(_dir);
    if (!album_cache || strcmp(album_cache->dir, _dir)) {
        album_cache  = seach_album_by_dir(C, _dir);
        if (!album_cache) {
            album_cache = album_new(basename(dname), _dir);
            list_del_init(&album_cache->node);
            list_add_tail(&album_cache->node, &C->albums_head);
        }
    }
    album_song_add(album_cache, S);
    free(_dir);
}

static void __get_all_songs_in_directory(Collections *C, const char *directory) {
    struct stat s;
    DIR *dir;
    struct dirent *dentry;
    if (!directory)
        return;
    dir = opendir(directory);
    Song *S;
    char *file, *ptr;
    if (!dir) {
        error("Cannot open dir '%s'\n", directory);
        return;
    }
    while ((dentry = readdir(dir))) {
        if (dentry->d_name[0] == '.')
            continue; // Ignore hidden files including . ..
        file = (char *)calloc(1, strlen(directory) + strlen(dentry->d_name) + 2);
        sprintf(file, "%s/%s", directory, dentry->d_name);
        if (stat(file, &s) < 0) {
            error("stat %s failed !!\n", file);
            free(file);
            continue;
        }
        ptr = check_file_mine_support(file);
        if (S_ISDIR(s.st_mode))
            __get_all_songs_in_directory(C, file);
        else if (ptr && ptr == (file + strlen(file) - 4)) { // FIXME: the file ext not necessary 3 letters
            S = song_new(file);
            list_add_tail(&S->all_song_node, &C->all_songs_head);
            list_add_tail(&S->shuffle_list_node, &C->shuffle_list_head);
        }
        free(file);
    }
    closedir(dir);
}



